package com.example.xcService.pojo.channels.impl;

import com.alibaba.fastjson.JSONObject;
import com.example.xcService.pojo.channels.ChannelResult;
import com.example.xcService.pojo.channels.ChannelService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import static org.apache.commons.codec.digest.MessageDigestAlgorithms.MD5;


/*
*  QQ绿钻
* */
@Slf4j
public class Plugin_ECARD_P032 implements ChannelService {
    //算法名称
    private static final String KEY_ALGORITHM = "desede";
    // 算法名称/加密模式/填充方式
    private static final String CIPHER_ALGORITHM = "DESede/CBC/NoPadding";

    public static void main(String[] args) {

        byte[] bytes ={0x6B,0x64,0x72,0x68,0x4E,0x3B,0x55,0x31};
        String str = new String(bytes, StandardCharsets.UTF_8);
        System.out.println(str);
    }

    @Override
    public ChannelResult QueryBalance(JSONObject apiConfig) {
        System.out.println("成功调用了QueryBalance");
        return ChannelResult.success("success");
    }
    /*sku  G_Y_1_dk   S_R_21   1.绿钻G/超级会员S  2.月Y/日R  3.数字(月数/天数)  4.dk(参与新用户抵扣活动)*/
    @Override
    public ChannelResult Submit(JSONObject apiConfig, Map<String, Object> data) {
        try {
            LinkedHashMap<String, Object> submitParams = new LinkedHashMap<>();
            //tran_seq 前台流水号  in_acct  用户帐号
            //dk 抵扣参数：通过解密info字段获得，如果是想参加这种模式
            //取值范围，目前只支持1，其他值为非法 填1，表示参与（新用户）打折活动
            //orderTime 订单时间（从19700101 00:00:00开始的秒数）
            LocalDate currentDate = LocalDate.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            String formattedDate = currentDate.format(formatter);//交易日期
            long currentTimeMillis = System.currentTimeMillis();
            long secondsSinceEpoch = currentTimeMillis / 1000;//订单时间 s
            String accountVal = String.valueOf(data.get("AccountVal"));
            String supProductId = String.valueOf(data.get("SUPProductId"));
            String[] s = supProductId.split("_");
            if(s.length<3){
                return ChannelResult.FAILED("商品编号配置错误,无法解析");
            }
            StringBuilder strInfo = new StringBuilder();
            strInfo.append("in_acct").append("=").append(data.get("AccountVal")).append("&")
                    .append("tran_seq").append("=").append(data.get("ChannelOrderCode")).append("&")
                    .append("orderTime").append("=").append(secondsSinceEpoch);
            if (s.length==4){
                strInfo.append("&").append("dk").append("=").append(1);
            }
            String key = apiConfig.getString("SecretKey");
            String keyIv = apiConfig.getString("KeyIv");
            byte[] byteKV = keyIv.getBytes(StandardCharsets.UTF_8);
            byte[] bytes = des3EncodeCBC(key, byteKV, strInfo.toString());
            String info = bytesToHexString(bytes);
            log.info("QQ绿钻加密前参数"+strInfo.toString());
            submitParams.put("info",info);
            submitParams.put("tran_date",formattedDate);//交易日期 YYYYMMDD
            String accountType = getAccountType(accountVal);
            submitParams.put("in_acct_type",accountType);
            if (s[0].equals("G")) {  //1 绿钻豪华  4 超级会员
                submitParams.put("sell_type",1);
                if (s[1].equals("Y")){ //月
                    submitParams.put("sell_sub_type",0);//0 一个月
                    if (Long.parseLong(s[2])>12){
                        return ChannelResult.FAILED("单次购买月卡最多12个月");
                    }
                    submitParams.put("num",s[2]); //x月
                }else { //日
                    submitParams.put("sell_sub_type",2);//  2为7天
                    if (Long.parseLong(s[2])%7!=0){
                        return ChannelResult.FAILED("商品编号配置错误(日期时间必须与7整除)");
                    }
                    submitParams.put("num",s[2]);  //x天
                }
            }else if (s[0].equals("S")){
                submitParams.put("sell_type",4);
                if (s[1].equals("Y")){
                    submitParams.put("sell_sub_type",0);//0 一个月
                    if (Long.parseLong(s[2])>12){
                        return ChannelResult.FAILED("单次购买月卡最多12个月");
                    }
                    submitParams.put("num",s[2]); //X月
                }else {
                    /*submitParams.put("sell_sub_type",2);// 2 为7天
                    if (Long.parseLong(s[2])%7!=0){
                        return ChannelResult.FAILED("商品编号配置错误(日期时间必须与7整除)");
                    }
                    submitParams.put("num",s[2]);  //X天*/
                    return ChannelResult.FAILED("超级会员暂不支持开通周卡");
                }
            }else {
                return ChannelResult.FAILED("商品编号配置错误");
            }

            if (s.length==4) {
                if (isPhoneNumber(accountVal)){
                    return ChannelResult.FAILED("折扣下单需使用QQ号");
                }
                    submitParams.put("ret_type",4);
            }else {
                    submitParams.put("ret_type",1);//默认价格充值
            }
            String encKey = apiConfig.getString("EncKey");
            String sign = signature(submitParams, encKey);
            submitParams.put("mch_id",apiConfig.getString("AppId"));

            /*平台渠道信息， 该参数不参与签名*/
            submitParams.put("chan","xx");
            submitParams.put("format","json");
            //数字签名
            submitParams.put("sign",sign);
            submitParams.put("suspend","1");

            String submitUrl = apiConfig.getString("SubmitUrl");
            String resStr = sendGet2(submitUrl, submitParams);
            JSONObject resObj = JSONObject.parseObject(resStr);
            String code = resObj.getString("code");
            String message = resObj.getString("message");
            if(code.equals("10106")){
                return ChannelResult.success("充值成功#但用户手机号未绑定");
            }
            if (code.equals("0")){
                return ChannelResult.IN_PROCESS("充值中");
            }
            if (code.equals("10002")|| code.equals("10010")) {//订单号重复
                return ChannelResult.IN_PROCESS("充值中");
            }
            if (code.equals("10004")) {
                return ChannelResult.error(message);
            }
            if (code.equals("10107")) {
                return ChannelResult.FAILED("折扣活动，用户身份不符合活动要求");
            }
            return ChannelResult.FAILED(code+"#"+message);
        }catch (Exception e) {
            e.printStackTrace();
            log.error("QQ绿钻提交订单接口错误：",e);
            return  ChannelResult.error("QQ绿钻提交订单接口错误");
        }
    }

    @Override
    public ChannelResult Query(JSONObject apiConfig, Map<String, Object> data) {

        try {
           /* LocalDate currentDate = LocalDate.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            String formattedDate = currentDate.format(formatter);//交易日期*/

            String creatTimeObj = String.valueOf(data.get("CreatTime"));
            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date date = inputFormat.parse(creatTimeObj);
            SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMdd");
            String formattedDate = outputFormat.format(date);

            StringBuilder infoStr = new StringBuilder();
            infoStr.append("tran_seq").append("=").append(data.get("ChannelOrderCode"))
                    .append("&").append("tran_date").append("=").append(formattedDate);

            String key = apiConfig.getString("SecretKey");
            String keyIv = apiConfig.getString("KeyIv");
            byte[] byteKV = keyIv.getBytes(StandardCharsets.UTF_8);

            byte[] bytes = des3EncodeCBC(key, byteKV, infoStr.toString());
            String info = bytesToHexString(bytes);

            LinkedHashMap<String, Object> queryParams = new LinkedHashMap<>();
            queryParams.put("info",info);
            String encKey = apiConfig.getString("EncKey");
            String sign = signature(queryParams, encKey);
            queryParams.put("sign",sign);
            queryParams.put("format","json");
            queryParams.put("mch_id",apiConfig.getString("AppId"));
            String queryUrl = apiConfig.getString("QueryUrl");
            log.info("QQ绿钻查询"+data.get("ChannelOrderCode"));
            String resStr = sendGet2(queryUrl, queryParams);
            JSONObject resObj = JSONObject.parseObject(resStr);
            String code = resObj.getString("code");
            String message = resObj.getString("message");
            if (code.equals("0")){
                JSONObject resData = resObj.getObject("data", JSONObject.class);
                String order_status = resData.getString("order_status");
                if (order_status.equals("1")){
                    return ChannelResult.success("充值成功");
                }
                if (order_status.equals("2")){
                    return ChannelResult.IN_PROCESS("充值中");
                }
                if (order_status.equals("3")){
                    return ChannelResult.FAILED("充值失败");
                }
            }
            return ChannelResult.error(message);
        }catch (Exception e) {
            e.printStackTrace();
            log.error("QQ绿钻查询订单接口错误：",e);
            return  ChannelResult.error("QQ绿钻查询订单接口错误");
        }

    }

    @Override
    public ChannelResult Notify(JSONObject apiConfig, Map<String, Object> data) {
        System.out.println("成功调用了Notify");
        return ChannelResult.success("success");
    }

    public static String getAccountType(String accountVal) {
        if (isPhoneNumber(accountVal)) {
            return "4";
        } else if (isQQNumber(accountVal)) {
            return "1";
        } else {
            return "1";
        }
    }
    private static boolean isPhoneNumber(String accountVal) {
        return accountVal.matches("\\d{11}");
    }
    private static boolean isQQNumber(String accountVal) {
        return accountVal.matches("\\d{5,12}");
    }
    private static boolean isWeChatNumber(String accountVal) {
        return accountVal.matches("^[-_a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}$");
    }


    /**
     * CBC加密
     * @param key 密钥
     * @param keyiv IV
     * @param data 明文
     * @return Base64编码的密文
     * @throws Exception
    key = "111111112222222233333333";
    keyiv = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38};
     */
    public static byte[] des3EncodeCBC(String key, byte[] keyiv, String data) throws Exception {
        Key deskey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ips = new IvParameterSpec(keyiv);
        cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);

        byte[] bb = data.getBytes();
        byte[] aa = null;
        if(bb.length%8>0){
            aa = new byte[(bb.length/8+1)*8];
            System.arraycopy(bb, 0, aa, 0, bb.length);
        }else{
            aa = bb;
        }

        byte[] bOut = cipher.doFinal(aa);
        return bOut;
    }

    /**
     *
     * 生成密钥key对象
     * @throws Exception
     */
    private static Key keyGenerator(String keyStr) throws Exception {
        DESedeKeySpec KeySpec = new DESedeKeySpec(keyStr.getBytes());
        SecretKeyFactory KeyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
        return ((Key) (KeyFactory.generateSecret(((java.security.spec.KeySpec) (KeySpec)))));
    }
    /**
     * CBC解密
     * @param key 密钥
     * @param keyiv IV
     * @param data Base64编码的密文
     * @return 明文
     * @throws Exception
     */
    public static byte[] des3DecodeCBC(byte[] key, byte[] keyiv, byte[] data) throws Exception {
        Key deskey = keyGenerator(new String(key));
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ips = new IvParameterSpec(keyiv);
        cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
        byte[] bOut = cipher.doFinal(data);
        return bOut;
    }

    /**
     * CBC解密
     *
     * @param key   密钥
     * @param keyiv IV
     * @param data  Base64编码的密文
     * @return 明文
     * @throws Exception
     */
    public static byte[] des3DecodeCBC(String key, byte[] keyiv, byte[] data) throws Exception {
        Key deskey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ips = new IvParameterSpec(keyiv);
        cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
        byte[] bOut = cipher.doFinal(data);
        return bOut;
    }

    /**
     * CBC解密
     *
     * @param key   密钥
     * @param keyiv IV
     * @param data  Base64编码的密文
     * @return 明文
     * @throws Exception
     */
    public static byte[] des3DecodeCBC(String key, byte[] keyiv, String data) throws Exception {
        byte[] dataByte = HexStringToBytes(data);
        Key deskey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ips = new IvParameterSpec(keyiv);
        cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
        byte[] bOut = cipher.doFinal(dataByte);
        return bOut;
    }


    /**
     * 从字节数组到十六进制字符串转换
     *
     * @return 十六进制字符串
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }


    /*将十六进制字符串转换为字节数组*/
    public static byte[] HexStringToBytes(String hexString) {
        if (hexString == null || hexString.length() < 2) {
            return null;
        }
        byte[] result = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            result[i / 2] = (byte) Integer.parseInt(hexString.substring(i, i + 2), 16);
        }
        return result;
    }


    public String signature(LinkedHashMap<String,Object> params,String encKey){
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        sb.deleteCharAt(sb.length() - 1); // 去掉最后一个&

        // 拼接enc_key使用||
        sb.append("||").append(encKey);

        return md5Encrypt(sb.toString());
    }
    private static String md5Encrypt(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance(MD5);
            byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : messageDigest) {
                hexString.append(String.format("%02x", b));
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Failed to get MD5 instance", e);
        }
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet2(String url, Map<String,Object> param) {
        String result = "";
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();

        StringBuffer params = new StringBuffer();
        param.forEach((key, value) -> {
            if (!StringUtils.isEmpty(params.toString()))
            {
                params.append("&");
            }
            try{
                params.append(URLEncoder.encode(key, "utf-8")+"=" + URLEncoder.encode(String.valueOf(value), "utf-8"));
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        log.info("Http Get请求："+url+"?"+param.toString());
        HttpGet httpGet = new HttpGet(url+"?"+params.toString());
        // 响应模型
        CloseableHttpResponse response = null;
        try {
            // 设置ContentType(注:如果只是传普通参数的话,ContentType不一定非要用application/json)
            httpGet.setHeader("Content-Type", "application/json;charset=utf8");
            // 由客户端执行(发送)Get请求
            response = httpClient.execute(httpGet);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();

           log.info("响应状态为:" + response.getStatusLine());
            if (responseEntity != null) {
                result= EntityUtils.toString(responseEntity);
                log.info("响应内容为:" + result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 释放资源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return  result;
    }


}
